unit DummyMriMain;
//
// DummyMRI
//
// The Dummy MRI application allows playback of a wav sound file associated with
//   one single TR of an accociated sequence at the MRI Avanto or Trio.
// No actual MRI sequence parameters can be adjusted (the wav soundfile should just
//   generate the proper sound for each TR.
//
// Only TR and measurements can be set to allow emulation of the measurement time
//   and number of scanned volumes
//
// For each volume a scan trigger pulse is generated via the attached BITSI box
// For each volume a wav sound file is played, emulating the scanner noise in the pace
// of TR.
//
// Note: If the playback time of the sound file is longer than the TR, the soundfile
//       is stopped, rewinded and replayed at the beginning of the next TR. This results
//       in an uninterrupted playback of the sound file. In such case the subject would
//       only hear the first TR-time segments of the complete sound file.
//
// Version history:
//
// Created version 1.1: May 10, 2010, Erik van den Boogert
// Debugged version 1.2: May 11, 2010, Erik van den Boogert
//

interface

uses Windows, Classes, Graphics, Forms, Controls, Menus,
  Dialogs, StdCtrls, Buttons, ExtCtrls, ComCtrls, ImgList, StdActns,
  ActnList, ToolWin, OoMisc, AdPort, MPlayer, Spin, SysUtils, IniFiles;

type
  TSDIAppForm = class(TForm)
    OpenDialog: TOpenDialog;
    SaveDialog: TSaveDialog;
    ToolBar1: TToolBar;
    ToolButton1: TToolButton;
    ToolButton2: TToolButton;
    ActionList1: TActionList;
    FileNew1: TAction;
    FileOpen1: TAction;
    FileSave1: TAction;
    FileSaveAs1: TAction;
    FileExit1: TAction;
    EditCut1: TEditCut;
    EditCopy1: TEditCopy;
    EditPaste1: TEditPaste;
    HelpAbout1: TAction;
    StatusBar: TStatusBar;
    ImageList1: TImageList;
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    FileOpenItem: TMenuItem;
    FileSaveItem: TMenuItem;
    FileSaveAsItem: TMenuItem;
    N1: TMenuItem;
    FileExitItem: TMenuItem;
    Help1: TMenuItem;
    HelpAboutItem: TMenuItem;
    comBITSI: TApdComPort;
    TimerRunMRI: TTimer;
    mpSound: TMediaPlayer;
    btnStartStop: TButton;
    lblTS: TLabel;
    lblTA: TLabel;
    panRoutine: TPanel;
    lblSoundFile: TLabel;
    lblComNumber: TLabel;
    edtComNumber: TSpinEdit;
    lblTR: TLabel;
    edtTR: TSpinEdit;
    lblMeasurements: TLabel;
    edtMeasurements: TSpinEdit;
    shpLineLeft1: TShape;
    shpLineRight: TShape;
    lblTRUnits: TLabel;
    shpLineLeft2: TShape;
    Shape4: TShape;
    TimerDisplay: TTimer;
    edtSoundFile: TEdit;
    chkPlaySoundFile: TCheckBox;
    OpenDialogSoundFile: TOpenDialog;
    btnBrowse: TButton;
    lblSoundFilePath: TLabel;
    procedure FileOpen1Execute(Sender: TObject);
    procedure FileSave1Execute(Sender: TObject);
    procedure FileExit1Execute(Sender: TObject);
    procedure HelpAbout1Execute(Sender: TObject);
    procedure TimerRunMRITimer(Sender: TObject);
    procedure edtSlicesChange(Sender: TObject);
    procedure edtTRChange(Sender: TObject);
    procedure edtMeasurementsChange(Sender: TObject);
    procedure edtConcatenationsChange(Sender: TObject);
    procedure btnStartStopClick(Sender: TObject);
    procedure TimerDisplayTimer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnBrowseClick(Sender: TObject);
  private
    { Private declarations }
    TAms : longint;
    TSms : longint;
    Time32Started: LongWord;
    MeasurementsCount: longint;
    DragInProgress : boolean;
    procedure UpdateTA;
    procedure UpdateTS;
    procedure OnChangeProtocolParam;
    procedure EnableEditing;
    procedure DisableEditing;
    procedure SendBITSITrigger;
    procedure PlayMRISound;
    procedure CloseComBITSI;
    procedure OpenComBITSI;
    procedure SaveInifile(IniFileName: string);
    procedure LoadInifile(IniFileName: string);
  public
    { Public declarations }
  end;

var
  SDIAppForm: TSDIAppForm;

implementation

uses about;

{$R *.dfm}


//--- Time format methods ------------------------------------------------------

Function RelTimeToStr(RelTime:Real): String;
const
  RErr = 0.00003;
var
  DPara,
  Min, Sec,
  RelTmInt       : Integer;
  MinStr, SecStr : string[3];
  FullTmStr      : string[11];
begin
  Str((RelTime + 10000 + RErr):5:2, FullTmStr);
  Val(Copy(FullTmStr,2,4), RelTmInt, DPara);
  Min := (RelTmInt div 60) + 100; Str(Min:3, MinStr);
  Sec := (RelTmInt mod 60) + 100; Str(Sec:3, SecStr);
  Result := Copy(MinStr,2,2)+':'+Copy(SecStr,2,2); //+Copy(FullTmStr,6,3);
end;

Function GetPCTime32:LongWord;
begin
  Result := DateTimeToTimeStamp(Time).Time;
end;

Function ExtractFileName(FileName: string): string;
begin
  Result := FileName;
  While (Pos('\',Result) > 0) do
  Result := Copy(Result,Pos('\',Result)+1,255);
end;


//--- Load & Save inifiles -----------------------------------------------------

procedure TSDIAppForm.SaveInifile(IniFileName: string);
var
  IniFile: TIniFile;
begin
  try
    IniFile := TIniFile.Create(IniFileName);
    IniFile.WriteString('DummyMRI','SoundFile', edtSoundFile.Text);
    IniFile.WriteString('DummyMRI','SoundFilePath', lblSoundFilePath.Caption);
    IniFile.WriteInteger('DummyMRI','TR', edtTR.Value);
    IniFile.WriteInteger('DummyMRI','Measurements', edtMeasurements.Value);
    IniFile.WriteBool('DummyMRI','PlaySoundFile', chkPlaySoundFile.checked);
    IniFile.WriteInteger('DummyMRI','BITSIComPort', edtComNumber.Value);
    IniFile.Free;
  except
    IniFile.Free;
    ShowMessage('Error: Saving settings failed.')
  end;
end;

procedure TSDIAppForm.LoadInifile(IniFileName: string);
var
  IniFile: TIniFile;
begin
  try
    IniFile := TIniFile.Create(IniFileName);
    edtSoundFile.text        := IniFile.ReadString('DummyMRI','SoundFile', 'myMRISound.wav');
    lblSoundFilePath.caption := IniFile.ReadString('DummyMRI','SoundFilePath', 'myMRISound.wav');
    edtTR.Value              := IniFile.ReadInteger('DummyMRI','TR', 2000);
    edtMeasurements.Value    := IniFile.ReadInteger('DummyMRI','Measurements', 100);
    chkPlaySoundFile.checked := IniFile.ReadBool('DummyMRI','PlaySoundFile', false);
    edtComNumber.Value       := IniFile.ReadInteger('DummyMRI','BITSIComPort', 1);
    IniFile.Free;
    if NOT FileExists(lblSoundFilePath.Caption) then
    begin
      chkPlaySoundFile.checked := false;
      ShowMessage('Warning: Sound file not found, sound playback disabled.');
    end;
  except
    IniFile.Free;
    ShowMessage('Error: Loading settings failed.');
  end;
end;

procedure TSDIAppForm.FileOpen1Execute(Sender: TObject);
begin
  if edtTR.Enabled then
  begin
    if OpenDialog.Execute then LoadIniFile(OpenDialog.FileName);
  end;
end;

procedure TSDIAppForm.FileSave1Execute(Sender: TObject);
begin
  if edtTR.Enabled then
  begin
    if SaveDialog.Execute then SaveIniFile(SaveDialog.FileName);
  end;
end;


//--- Create, Close en About methods -------------------------------------------

procedure TSDIAppForm.FormCreate(Sender: TObject);
begin
  OpenComBITSI;
  SendBITSITrigger;
end;

procedure TSDIAppForm.FileExit1Execute(Sender: TObject);
begin
  Close;
end;

procedure TSDIAppForm.HelpAbout1Execute(Sender: TObject);
begin
  AboutBox.ShowModal;
end;


//--- BITSI ComPort methods ----------------------------------------------------

procedure TSDIAppForm.btnBrowseClick(Sender: TObject);
begin
  if OpenDialogSoundFile.execute then
  begin
    lblSoundFilePath.Caption := OpenDialogSoundFile.FileName;
    edtSoundFile.Text := ExtractFileName(lblSoundFilePath.Caption);
  end;
end;


procedure TSDIAppForm.CloseComBITSI;
begin
  try comBITSI.Open := false; except end;
end;

procedure TSDIAppForm.OpenComBITSI;
begin
  comBITSI.ComNumber := edtComNumber.Value;
  try
    comBITSI.Open := true;
  except
    ShowMessage('Error: BITSI Com-Port '+ IntToStr(edtComNumber.Value) +' could not be opened');
  end;
end;

procedure TSDIAppForm.SendBITSITrigger;
begin
  try
    comBITSI.PutChar(Chr(1));
  except end;
end;


//--- mpSound methods ----------------------------------------------------------

procedure TSDIAppForm.PlayMRISound;
begin
  try
    mpSound.Stop;
    mpSound.Rewind;
    mpSound.Play;
  except end;
end;


//--- Update TA & TA labels ----------------------------------------------------

procedure TSDIAppForm.UpdateTA;
begin
  TAms := edtMeasurements.value * edtTR.value;
  lblTA.Caption := 'TA: ' + RelTimeToStr(TAms/1000);
end;

procedure TSDIAppForm.UpdateTS;
begin
  //--- initialize TS = TA
  TSms := TAms;
  //--- only when running count down residual time using GetPCTime32
  if btnStartStop.Caption = 'Stop' then TSms := TAms - (GetPCTime32 - Time32Started);
  //--- ensure TS doesn't underrun 0 s.
  if (TSms < 0) then TSms := 0;
  lblTS.Caption := 'Scanning: ' + RelTimeToStr(TSms/1000);
end;

procedure TSDIAppForm.OnChangeProtocolParam;
begin
  UpdateTA;
end;

procedure TSDIAppForm.TimerDisplayTimer(Sender: TObject);
begin
  UpdateTA;
  UpdateTS;
end;

procedure TSDIAppForm.edtMeasurementsChange(Sender: TObject);
begin
  OnChangeProtocolParam;
end;

procedure TSDIAppForm.edtConcatenationsChange(Sender: TObject);
begin
  OnChangeProtocolParam;
end;

procedure TSDIAppForm.edtSlicesChange(Sender: TObject);
begin
  OnChangeProtocolParam;
end;

procedure TSDIAppForm.edtTRChange(Sender: TObject);
begin
  OnChangeProtocolParam;
end;


//--- Prepare, Start & Stop methods --------------------------------------------

procedure TSDIAppForm.DisableEditing;
begin
  edtMeasurements.Enabled := false;
  edtTR.Enabled := false;
  edtComNumber.Enabled := false;
  chkPlaySoundFile.Enabled := false;
end;

procedure TSDIAppForm.EnableEditing;
begin
  edtMeasurements.Enabled := true;
  edtTR.Enabled := true;
  edtComNumber.Enabled := true;
  chkPlaySoundFile.Enabled := true;
end;

procedure TSDIAppForm.btnStartStopClick(Sender: TObject);
begin
  //--- Case Prepare
  if btnStartStop.Caption = 'Prepare' then
  begin
    if comBITSI.ComNumber <> edtComNumber.Value then
    begin
      CloseComBITSI;
      OpenComBITSI;
    end;
    //
    if NOT FileExists(lblSoundFilePath.Caption) then
    begin
      chkPlaySoundFile.checked := false;
      ShowMessage('Warning: Sound File does not exist, sound playback disabled');
    end;
    //
    if (chkPlaySoundFile.checked) then
    begin
      mpSound.Close;
      mpSound.FileName := lblSoundFilePath.Caption;
     try mpSound.Open;
      except
        chkPlaySoundFile.checked := false;
        ShowMessage('Error: Sound File could not be loaded, sound playback disabled.');
      end;
    end;
    //
    if comBITSI.Open then
    begin
      DisableEditing;
      btnStartStop.Caption := 'Start';
    end;
  end else
  //--- Case Start
  if btnStartStop.Caption = 'Start' then
  begin
    Time32Started := GetPCTime32;
    MeasurementsCount := edtMeasurements.Value;
    DisableEditing;
    timerRunMRI.Interval := edtTR.Value;
    timerRunMRI.Enabled := true;
    SendBITSITrigger;
    if chkPlaySoundFile.checked then PlayMRISound;
    btnStartStop.Caption := 'Stop';
  end else
  //--- Case Stop
  begin
    EnableEditing;
    timerRunMRI.Enabled := false;
    btnStartStop.Caption := 'Prepare';
  end;
end;


//--- Timer loop running the MRI sequence --------------------------------------

procedure TSDIAppForm.TimerRunMRITimer(Sender: TObject);
begin
  Dec(MeasurementsCount);
  if (MeasurementsCount <= 0) then
  begin
    btnStartStopClick(Sender);
  end else
  begin
    SendBITSITrigger;
    if chkPlaySoundFile.checked then PlayMRISound;
  end;
end;

end.



